package com.gitee.mrj.aiboot.core.sf.schema;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.gitee.mrj.aiboot.core.sf.FormDefinitionGeneratorFactory;
import com.gitee.mrj.aiboot.core.sf.generators.UIForm;
import com.gitee.mrj.aiboot.core.util.ReflectionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.OrderUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public final class UiFormSchemaGenerator {

    /**
     * 根据CLASS对象生成json schema
     *
     * @param formDto
     * @return
     */
    public static UIForm generate(Class formDto) {
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode rootSchema = mapper.createObjectNode();
        ObjectNode rootUi = mapper.createObjectNode();
        ObjectNode rootClassUi = mapper.createObjectNode();

        UIForm uiForm = new UIForm(mapper, rootSchema, rootUi, 1);
        UIForm rootUiForm = new UIForm(mapper, rootSchema, rootClassUi, 1);

        buildProperties(formDto, rootUiForm);
        if (rootUiForm.getUi().size() > 0) {
            uiForm.getUi().set("*", rootUiForm.getUi());
        }

        List<Field> fields = ReflectionUtils.findAllAlainFields(formDto);
        buildOrder(fields, rootClassUi);

        Map<Field, UIForm> nodeMap = initFieldsFormDefinition(uiForm, fields);
        ObjectNode properties = rootSchema.putObject("properties");
        nodeMap.forEach((key, form) -> {
            properties.set(key.getName(), form.getSchema());
            if (form.getUi().size() > 0) {
                rootUi.set("$" + key.getName(), form.getUi());
            }
        });

        return uiForm;
    }

    public static Map<Field, UIForm> initFieldsFormDefinition(UIForm parent, List<Field> declaredFields) {
        Map<Field, UIForm> nodes = new HashMap<>();
        declaredFields.forEach(field -> buildProperty(nodes, parent, field));
        return nodes;
    }

    private static void buildProperty(Map<Field, UIForm> nodes, UIForm parent, Field field) {
        MergedAnnotations annotations = MergedAnnotations.from(field);
        ObjectNode node = parent.getMapper().createObjectNode();
        ObjectNode nodeUi = parent.getMapper().createObjectNode();
        UIForm property = new UIForm(parent.getMapper(), node, nodeUi, parent.getLevel() + 1);
        annotations.stream().sorted(getComparator()).forEach(annotation -> buildProperty(parent, field, annotation, property));
        if (node.size() > 0) {
            nodes.put(field, property);
        }
    }

    /**
     * 排序规则
     *
     * @return
     */
    private static Comparator<MergedAnnotation<Annotation>> getComparator() {
        return (s1, s2) -> {
            Integer order1 = OrderUtils.getOrder(s1.getType());
            Integer order2 = OrderUtils.getOrder(s2.getType());
            if (Objects.isNull(order1)) {
                order1 = Integer.MAX_VALUE;
            }
            if (Objects.isNull(order2)) {
                order2 = Integer.MAX_VALUE;
            }
            return NumberUtils.compare(order1, order2);
        };
    }

    public static void buildProperties(Class clazz, UIForm uiForm) {
        MergedAnnotations annotations = MergedAnnotations.from(clazz);
        annotations.stream().sorted(getComparator()).forEach(annotation -> buildProperties(clazz, annotation, uiForm));
    }

    private static void buildProperties(Class clazz, MergedAnnotation<Annotation> annotation, UIForm uiForm) {
        FormDefinitionGeneratorFactory.getInstance().getGenerator(annotation.getType())
                .ifPresent(list -> {
                    list.forEach(generator -> {
                        generator.generate(uiForm, clazz);
                    });
                });
    }

    private static void buildProperty(UIForm root, Field field, MergedAnnotation<Annotation> annotation, UIForm uiForm) {
        FormDefinitionGeneratorFactory.getInstance().getGenerator(annotation.getType())
                .ifPresent(list -> {
                    list.forEach(generator -> {
                        generator.generate(root, field, uiForm);
                    });
                });
    }

    public static void buildOrder(List<Field> fields, ObjectNode schema) {
        List<String> orders = fields.stream().filter(field -> {
            return null != OrderUtils.getOrder(field);
        }).sorted((f1, f2) -> {
            Integer sf1Order = OrderUtils.getOrder(f1);
            Integer sf2Order = OrderUtils.getOrder(f2);
            return NumberUtils.compare(sf1Order, sf2Order);
        }).map(field -> field.getName()).collect(Collectors.toList());
        ArrayNode arrayNode = schema.putArray("order");
        orders.forEach(field -> arrayNode.add(field));
        if (orders.size() != fields.size()) {
            arrayNode.add("*");
        }
    }

    private UiFormSchemaGenerator() {
    }
}
